﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Threading;
using Lastfm.Services;

namespace LibraryImport
{
    public partial class Frontend : Form
    {
        #region General

        public Frontend()
        {
            InitializeComponent();
            LastfmClient.Initialize();
        }

        /// <summary>
        /// Checks if any background process is running
        /// </summary>
        private bool isWorkersBusy()
        {
            if (ImportArtists.IsBusy || LoadFiles.IsBusy)
                return true;
            return false;
        }

        /// <summary>
        /// If background image loading threads are still working
        /// - it's time to kill them!
        /// </summary>
        private void Frontend_FormClosed(object sender, FormClosedEventArgs e)
        {
            if(ArtistImageList.Images.Count < ArtistList.Items.Count)
                System.Diagnostics.Process.GetCurrentProcess().Kill();
        }

        /// <summary>
        /// To avoid terrible border on buttons when focused
        /// </summary>
        private void RemoveFocus(object sender, EventArgs e)
        {
            ActiveControl = ArtistList;
        }

        #endregion

        #region Menu Panel

        #region AddFolder Button

        /// <summary>
        /// Provides user to choose a ONE folder wirh audio files, then a loading thread is started
        /// </summary>
        private void AddFolder_Click(object sender, EventArgs e)
        {
            if ((AddFolderDialog.ShowDialog() == DialogResult.OK) && (isWorkersBusy() == false))
            {
                ListTip.Hide();
                LoadIndicator.Show();
                LoadIndicator.Refresh();

                LoadFiles.RunWorkerAsync(new String[1] { AddFolderDialog.SelectedPath });
            }
        }

        /// <summary>
        /// Changes button's background image when mouse is over or out the control
        /// </summary>
        private void AddFolder_MouseEnter(object sender, EventArgs e)
        {
            AddFolder.BackgroundImage = global::LibraryImport.Properties.Resources.FocusedButton;
        }
        private void AddFolder_MouseLeave(object sender, EventArgs e)
        {
            AddFolder.BackgroundImage = global::LibraryImport.Properties.Resources.NonFocusedButton;
        }

        #endregion

        #region Import Button

        /// <summary>
        /// If user is logged in - we are ready to import artistst to the user library, else - show a notification
        /// </summary>
        private void Import_Click(object sender, EventArgs e)
        {
            if ((ArtistList.Items.Count > 0) && (LoadFiles.IsBusy == false) && (ImportArtists.IsBusy == false))
            {
                LoadIndicator.Show();
                LoadIndicator.Refresh();

                String[] ArtistNames = new String[ArtistList.Items.Count];
                
                for (int i = 0; i < ArtistList.Items.Count; i++)
                {
                    ArtistNames[i] = ArtistList.Items[i].Text;
                }

                ImportArtists.RunWorkerAsync(ArtistNames);
            }
        }

        /// <summary>
        /// Changes button's background image when mouse is over or out the control
        /// </summary>
        private void Import_MouseEnter(object sender, EventArgs e)
        {
            Import.BackgroundImage = global::LibraryImport.Properties.Resources.FocusedButton;
        }
        private void Import_MouseLeave(object sender, EventArgs e)
        {
            Import.BackgroundImage = global::LibraryImport.Properties.Resources.NonFocusedButton;
        }

        #endregion
        
        #region Fields Username, Password, Login Button
        
        /// <summary>
        /// If username&password are correct hides all this controls and then MenuPanel_Paint draws "Logged in", 
        /// else - show notification 
        /// </summary>
        private void Login_Click(object sender, EventArgs e)
        {
            if (Login.Text == "Login")
            {
                String result = LastfmClient.Authenticate(Username.Text, Lastfm.Utilities.md5(Password.Text));
                
                if (result == null)
                {
                    Username.Hide();
                    Password.Hide();
                    
                    Login.AutoSize = true;
                    Login.Text = "     " + LastfmClient.GetUsername();
                    Login.TextAlign = ContentAlignment.MiddleRight;
                    Login.BackgroundImage = global::LibraryImport.Properties.Resources.LoggedButton;
                }
                else
                {
                    BadAuthentication.Show(result, Password, -25, -40);
                }
            }
            else
            {
                UserContextMenu.Show(Login, 0, Login.Height + 1);
            }
        }

        /// <summary>
        /// User can log out or see his page on Last.fm
        /// </summary>
        private void UserContextMenu_Click(object sender, EventArgs e)
        {
            if (Logout.Selected == true)
            {
                LastfmClient.Initialize();
                UserContextMenu.Close();

                Username.Show();
                Password.Show();

                Login.AutoSize = false;
                Login.Text = "Login";
                Login.TextAlign = ContentAlignment.MiddleCenter;
                Login.BackgroundImage = global::LibraryImport.Properties.Resources.LoginButton;
            }
            else if (Account.Selected == true)
            {
                System.Diagnostics.Process.Start(LastfmClient.GetUserURL());
            }
        }

        /// <summary>
        /// Some usability =)))
        /// </summary>
        private void Username_Enter(object sender, EventArgs e)
        {
            Username.Text = "";
            BadAuthentication.Hide(Password);
        }
        private void Username_Leave(object sender, EventArgs e)
        {
            if (Username.Text == "")
                Username.Text = "Username";
        }
        private void Password_Enter(object sender, EventArgs e)
        {
            Password.Text = "";
            BadAuthentication.Hide(Password);
        }
        private void Password_Leave(object sender, EventArgs e)
        {
            if (Password.Text == "")
                Password.Text = "Password";
        }
        private void Password_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                Login_Click(sender, new EventArgs());
        }

        #endregion

        #endregion

        #region Artist Panel

        /// <summary>
        /// Draws a border like on last.fm
        /// </summary>
        private void ArtistPanel_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(Pens.LightGray, 0, 0, ArtistPanel.Width - 1, ArtistPanel.Height - 1);
        }

        public static int ProgressPercentage;

        /// <summary>
        /// Draws percentage in the round indicator
        /// </summary>
        private void LoadIndicator_Paint(object sender, PaintEventArgs e)
        {
            Font font = new Font(FontFamily.GenericSansSerif, 9.0f, FontStyle.Bold);
            Point point = new Point((ProgressPercentage > 9) ? 14 : 18, 16); 
            e.Graphics.DrawString(ProgressPercentage.ToString(), font, Brushes.Gray, point);
        }
        
        #region Artist List
        
        /// <summary>
        /// Gives permission to dragdrop operation
        /// </summary>
        private void Artists_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false) && (isWorkersBusy() == false))
                e.Effect = DragDropEffects.All;
        }

        /// <summary>
        /// In this case we can choose several folders as well as single files
        /// </summary>
        private void Artists_DragDrop(object sender, DragEventArgs e)
        {
            ListTip.Hide();
            LoadIndicator.Show();
            LoadIndicator.Refresh();

            LoadFiles.RunWorkerAsync(e.Data.GetData(DataFormats.FileDrop));
        }

        /// <summary>
        /// Draws: 1. Selection (if exists) 2. Image (if loaded) 3. Underline
        /// </summary>
        private void Artists_DrawItem(object sender, DrawListViewItemEventArgs e)
        {
            if (e.Item.Selected == true)
            {
                e.Item.BackColor = Color.LightBlue;
                e.DrawBackground();
            }

            if (e.Item.ImageList.Images.ContainsKey(e.Item.Text) == true)
            {
                e.Item.ImageList.Draw(e.Graphics, e.Item.Position.X - 4, e.Item.Position.Y, e.Item.ImageList.Images.IndexOfKey(e.Item.Text));
            }
            else
            {
                e.Item.ImageList.Draw(e.Graphics, e.Item.Position.X - 4, e.Item.Position.Y, 0);
            }

            e.Graphics.DrawLine(Pens.LightGray, e.Item.Bounds.Left, e.Item.Bounds.Bottom - 1, e.Item.Bounds.Left + 468, e.Item.Bounds.Bottom - 1);
        }

        /// <summary>
        /// Draws the text of the third column "ArtistName"
        /// </summary>
        private void Artists_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
        {
            if (e.ColumnIndex == ArtistName.Index)
                e.DrawText(TextFormatFlags.VerticalCenter);
        }

        /// <summary>
        /// Provides user to delete some artists if necessary
        /// </summary>
        private void DeleteArtist_Click(object sender, EventArgs e)
        {
            if(ArtistList.Items.Count > 0)
                ArtistList.SelectedItems[0].Remove();
        }
        private void ArtistList_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.KeyCode == Keys.Delete) && (ArtistList.SelectedItems.Count > 0))
                ArtistList.SelectedItems[0].Remove();

            if ((e.KeyCode == Keys.V) && (e.Control == true))
                AddFiles_Click(sender, new EventArgs());
        }

        /// <summary>
        /// Allows user to add files from the clipboard
        /// </summary>
        private void AddFiles_Click(object sender, EventArgs e)
        {
            if ((Clipboard.ContainsFileDropList() == true) && (isWorkersBusy() == false))
            {
                ListTip.Hide();
                LoadIndicator.Show();
                LoadIndicator.Refresh();

                String[] Files = new String[Clipboard.GetFileDropList().Count];
                Clipboard.GetFileDropList().CopyTo(Files, 0);
                LoadFiles.RunWorkerAsync(Files);
            }                
        }

        #endregion

        #endregion

        #region Background Image Loading

        /// <summary>
        /// A sort of semaphore
        /// </summary>
        static Object ArtistAccess = new Object();

        /// <summary>
        /// Load Artist images in a background thread, because this operation takes much time
        /// </summary>
        /// <param name="e">Must contain artist names</param>
        private void LoadArtistImages_DoWork(object Data)
        {
            String[] ArtistNames = Data as String[];
            Array.Sort<String>(ArtistNames);

            WebClient wc = new WebClient();

            for (int i = 0; i < ArtistNames.Length; i++)
            {
                try
                {
                    Byte[] ImageData = wc.DownloadData(LastfmClient.GetImageURL(ArtistNames[i]));

                    lock (ArtistAccess)
                    {
                        ArtistImageList.Images.Add(ArtistNames[i], Image.FromStream(new MemoryStream(ImageData, 0, ImageData.Length)));
                        ArtistList.Invalidate();
                    }
                }
                catch (Exception){}
            }

            wc.Dispose();
        }       

        #endregion

        #region Background File Loading

        /// <summary>
        /// Runs background file loading and shows an indicator - for calm =)))
        /// </summary>
        /// <param name="e">Must contain Folder names</param>
        private void LoadFiles_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = Artists.GetArtistNames(new List<String>(e.Argument as String[]));
        }

        /// <summary>
        /// When completed hides indecator, starts background image loading and adds artists to the list
        /// </summary>
        /// <param name="e">Must contain Artist names</param>
        private void LoadFiles_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            LoadIndicator.Hide();
            ProgressPercentage = 0;

            String[] ArtistNames = e.Result as String[];
            new Thread(LoadArtistImages_DoWork).Start(ArtistNames);

            for (int i = 0; i < ArtistNames.Length; i++)
            {
                if ((ArtistList.FindItemWithText(ArtistNames[i]) == null) && (ArtistNames[i] != ""))
                {
                    ListViewItem item = ArtistList.Items.Add(ArtistNames[i]);
                    item.UseItemStyleForSubItems = true;
                    item.SubItems.Add("space");
                    item.SubItems.Add(ArtistNames[i]);
                }
            }
        }

        #endregion

        #region Background Artist Importing

        /// <summary>
        /// Starts importing to the library
        /// </summary>
        /// <param name="e">Must contain Artist names</param>
        private void ImportArtists_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = LastfmClient.ImportLibrary(e.Argument as String[]);
        }

        /// <summary>
        /// When completed clears the list and shows an encouraged text =)))
        /// </summary>
        private void ImportArtists_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            LoadIndicator.Hide();
            ProgressPercentage = 0;

            if ((e.Result as String) == null)
            {
                ArtistList.Items.Clear();
                ListTip.Text = "      Artists have been successfully imported";
                ListTip.Show();
            }
            else
            {
                BadAuthentication.Show(e.Result as String, Import, 0, -30);
            }
        }

        #endregion
     }
}